/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski   *
 *   g.latarowski@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include "graph.h"


// ===========================================================================
//                                 GNode
// ===========================================================================


// --------------------------- GNode::GNode() --------------------------------
GNode::GNode( GGraph * pGraph, bool removable )
	: QGraphicsItem( pGraph ), _pGraph( pGraph )
{
	setFlags( QGraphicsItem::ItemIsMovable );
	QGraphicsItem::setCursor( QCursor( Qt::OpenHandCursor ) );

	_removable = removable;
	
	_removeAction = new QAction( QIcon( ":/res/button_cancel.png" ),
								 tr( "&Remove node" ),
								 this );

	connect( _removeAction, SIGNAL( triggered() ),
			 this, SLOT( removeNode() ) );
}
// ---------------------------------------------------------------------------

    
// ------------------------ GNode::boundingRect() ----------------------------
QRectF GNode::boundingRect() const
{
	qreal adjust = 2;
	return QRectF( -8 - adjust, -8 - adjust,
					 16 + adjust,  16 + adjust );
}
// ---------------------------------------------------------------------------


// ------------------------------- GNode::shape() ----------------------------
QPainterPath GNode::shape() const
{
	QPainterPath path;
	path.addRect(-8, -8, 16, 16 );
	return path;
}
// ---------------------------------------------------------------------------


// ---------------------------- GNode::paint() -------------------------------
void GNode::paint( QPainter * pPainter,
				   const QStyleOptionGraphicsItem * option,
				   QWidget * widget )
{
	Q_UNUSED( widget );

	QColor color = _pGraph->color().dark();
	if (option->state & QStyle::State_Sunken )
		color = _pGraph->color().light();
	
	color.setAlpha( 180 );
	pPainter->setBrush( color );
	pPainter->setPen( QPen( Qt::black ) );
	pPainter->drawRect( -8, -8, 16, 16 );
}
// ---------------------------------------------------------------------------


// ------------------------- GNode::itemChange() -----------------------------
QVariant GNode::itemChange(GraphicsItemChange change, const QVariant &value)
{
	switch (change)
	{
		case ItemPositionChange:
		{
			if( !_pGraph ) break;
			
			QPointF newPos = value.toPointF();
				// keep node inside sceneRect
				// this helps detecting intesecting marker with _boundingRect edges
			double extra = 1;
			QRectF rect = _pGraph->boundingRect().adjusted( extra, extra, -extra, -extra );
			if( !rect.contains( newPos ) )
			{
				newPos.setX( qMin( rect.right(), qMax( newPos.x(), rect.left() ) ) );
				newPos.setY( qMin( rect.bottom(), qMax( newPos.y(), rect.top() ) ) );
			}
			
				// update marker
			_pGraph->adjust();
			
			return newPos;
		}
        	break;
		default:
			break;
	};
	
	return QGraphicsItem::itemChange(change, value);
}
// ---------------------------------------------------------------------------


// ----------------------------- GNode::mouseMoveEvent() ---------------------
void GNode::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
{
	if( !_pGraph ) return;
	
	QPointF newPos = event->scenePos();
		// keep hook inside shrunked a bit sceneRect
		// this helps detecting intesecting marker with _boundingRect edges
	double extra = 1;
	QRectF rect = _pGraph->boundingRect().adjusted( extra, extra, -extra, -extra );
	if( !rect.contains( newPos ) )
	{
		newPos.setX( qMin( rect.right(), qMax( newPos.x(), rect.left() ) ) );
		newPos.setY( qMin( rect.bottom(), qMax( newPos.y(), rect.top() ) ) );
	}
	
	setPos( newPos );
	
		// update graph
	_pGraph->adjust();
}
// ---------------------------------------------------------------------------


// ---------------------------- GNode::removeNode() --------------------------
void GNode::removeNode()
{
	if( _pGraph ) _pGraph->removeNode( this );
}
// ---------------------------------------------------------------------------


// ------------------------ GNode::contextMenuEvent() ------------------------
void GNode::contextMenuEvent( QGraphicsSceneContextMenuEvent * event )
{
	if( removable() )
	{
		QMenu menu;
		menu.addAction(_removeAction);
		menu.exec(event->screenPos());
	}
}
// ---------------------------------------------------------------------------


// ------------------- GNode::mousePressEvent() ------------------------------
void GNode::mousePressEvent( QGraphicsSceneMouseEvent * event)
{
		// set dragging cursor
	QGraphicsItem::setCursor( QCursor( Qt::ClosedHandCursor ) );
	
	QGraphicsItem::mousePressEvent( event );
}
// ---------------------------------------------------------------------------


// ------------------ GNode::mouseReleaseEvent() -----------------------------
void GNode::mouseReleaseEvent( QGraphicsSceneMouseEvent * event)
{
		// unset dragging cursor
	QGraphicsItem::setCursor( QCursor( Qt::OpenHandCursor ) );
	
	QGraphicsItem::mouseReleaseEvent( event );
}
// ---------------------------------------------------------------------------





// ===========================================================================
//                                 GEdge
// ===========================================================================


// --------------------------- GEdge::GEdge() --------------------------------
GEdge::GEdge( GGraph * pGraph, GNode * pNode1, GNode * pNode2,
			const bool clickable )
	: QGraphicsItem( pGraph ), _pGraph( pGraph ),
		_pNode1( pNode1 ), _pNode2( pNode2 )
{
	setFlags( ItemIsSelectable );
	setClickable( clickable );
}
// ---------------------------------------------------------------------------


// ---------------------- GEdge::setClickable() ------------------------------
void GEdge::setClickable( const bool clickable )
{
	_clickable = clickable;

		// if clickable then set cursor that indicates that otherwise
		// set regular arrow cursor 
	if( _clickable )
		QGraphicsItem::setCursor( QCursor( Qt::CrossCursor ) );
	else
		QGraphicsItem::setCursor( QCursor( Qt::ArrowCursor ) );
		
}
// ---------------------------------------------------------------------------


// ------------------------ GEdge::boundingRect() ----------------------------
QRectF GEdge::boundingRect() const
{
	qreal extra = 2;
	return QRectF( _start.x(), _start.y(),
				   _end.x() - _start.x(), _end.y() - _start.y() )
			.adjusted( -extra, -extra, extra, extra );
}
// ---------------------------------------------------------------------------


// -------------------------- GEdge::shape() ---------------------------------
QPainterPath GEdge::shape() const
{
	double extra = 6;
	
	// create strip of the size 2*extra
	
	QLineF forward( _start, _end );
	QLineF fNormal = forward.normalVector();
	fNormal.setLength( extra );
	QPointF p1 = fNormal.p2();
	fNormal.setLength( -2*extra );
	QPointF p2 = fNormal.p2();
	
	QLineF backward( _end, _start );
	QLineF bNormal = backward.normalVector();
	bNormal.setLength( extra );
	QPointF p3 = bNormal.p2();
	bNormal.setLength( -2*extra );
	QPointF p4 = bNormal.p2();
	
	QPainterPath shape( p1 );
	shape.lineTo( p2 );
	shape.lineTo( p3 );
	shape.lineTo( p4 );
	shape.closeSubpath();
	return shape;
}
// ---------------------------------------------------------------------------


// ---------------------------- GEdge::paint() -------------------------------
void GEdge::paint( QPainter * pPainter,
				   const QStyleOptionGraphicsItem * option,
				   QWidget * widget )
{
	Q_UNUSED( widget );
	Q_UNUSED( option );
	
	if( _pGraph ) pPainter->setBrush( _pGraph->color() );
	pPainter->setPen( QPen( Qt::black, 2 ) );
	pPainter->drawLine( _start, _end );
}
// ---------------------------------------------------------------------------


// ------------------------ GEdge::adjust() ----------------------------------
void GEdge::adjust()
{
	if( !_pNode1 || !_pNode2 ) return;
	
	removeFromIndex();
	_start = mapFromScene( _pNode1->pos() );
	_end   = mapFromScene( _pNode2->pos() );
	addToIndex();
}
// ---------------------------------------------------------------------------


// ----------------------- GEdge::mouseDoubleClickEvent() --------------------
void GEdge::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * event )
{
		// if edge isn't clickable then return
	if( !_clickable) return;
	
	if( _pNode1 )
		_pGraph->addNode( _pNode1, event->scenePos() );
}
// ---------------------------------------------------------------------------





// eof
